
import { error, Node, tween, Tween, UIOpacity, Vec3 } from 'cc';
import { EDITOR } from 'cc/env';
import { Action } from './Action';
import { ActionProp } from './ActionProp';
import { BaseAction } from './BaseAction';
import Tools from './Tools';
import { AcitonType, Easing, PropType, ValueType } from './Values';

/**
 * Action属性处理工具类
 */
export class ActionPropHandler {
    /**
     * 使用action填充tween
     */
    public static handleTween(tweenEntity: Tween<Node>, action: Action): void {
        let flag = 1;
        let propTweens = [];
        let actionFlag = action.actionFlag;

        // 将配置项转换tween实例
        while (AcitonType[flag]) {
            if (actionFlag & flag) {
                let handler = this.getHandlerClass(flag);
                let t = handler.handleProp(action);
                propTweens.push(t);
            }
            flag <<= 1;
        }

        // 将转换的tween实例与tweenEntity进行拼接
        if (propTweens.length) {
            if (propTweens.length === 1) {
                if (EDITOR && (actionFlag & AcitonType.opacity)) {
                    // 这是一个特殊处理。在编辑器模式下，如果只是缓动opacity，看不到效果。
                    // 需要同时对节点的某个属性进行缓动，于是这里增加了对position的缓动。
                    action.node.position = action.node.position.clone();
                    let tween2 = tween().by(action.duration, { position: new Vec3(0, 0, 0) }, { easing: "linear" });
                    propTweens.push(tween2);
                    tweenEntity.parallel(...propTweens);
                } else {
                    tweenEntity.then(propTweens[0]);
                }
            } else {
                tweenEntity.parallel(...propTweens);
            }
        }
    }

    /**
     * 重置属性值到开始值
     */
    public static resetToStartValue(action: Action): void {
        let actionFlag = action.actionFlag;

        let flag = 1;
        while (AcitonType[flag]) {
            if (actionFlag & flag) {
                let handler = this.getHandlerClass(flag);
                handler.resetToStartValue(action);
            }
            flag <<= 1;
        }
    }

    /**
     * 获得动作类型对应的的处理类
     */
    public static getHandlerClass(flag: AcitonType) {
        switch (flag) {
            case AcitonType.position:
                return PositionPropHandler;
            case AcitonType.opacity:
                return OpacityPropHandler;
            case AcitonType.scale:
                return ScalePropHandler;
            case AcitonType.angle:
                return AnglePropHandler;
            default:
                break;
        }
    }
}

/**
 * 动作属性处理器基类
 * 通过继承这个类,可以实现操作节点上不同的属性.但只能操作通过node.xxx能取到的值,比如position scale.
 * 如果希望操作其他属性(如opacity),则需要重写相关函数,实现不同的处理方法.
 */
class BasePropHandler {
    // 对应在Action的动作属性名 **必需重写**
    protected static PropName = "";
    // 获得动作属性对象 **必需重写**
    public static getActionProp(action: Action): ActionProp { return null; }

    /**
     * 获得属性值
     */
    public static getPropValue(action: BaseAction) {
        return action.node[this.PropName];
    }
    /**
     * 设置属性值
     */
    public static setPropValue(action: BaseAction, value: any) {
        action.node[this.PropName] = value;
    }

    /**
     * 重置属性值到开始值
     */
    public static resetToStartValue(action: Action) {
        let recordValue = action.propRecord[this.PropName];
        let actionProp = this.getActionProp(action);
        let propType = actionProp.type;
        let startValue;

        if (propType === PropType.from || propType === PropType.free) {
            if (actionProp.valueType === ValueType.absolute) {
                startValue = actionProp.valueFrom;
            } else if (actionProp.valueType === ValueType.relative) {
                startValue = Tools.add(recordValue, actionProp.valueFrom);
            }
        } else if (propType === PropType.to) {
            startValue = recordValue;
        }

        this.setPropValue(action, Tools.getClone(startValue));
    }

    /**
     * 获得属性值的结束值
     */
    public static getEndValue(action: Action) {
        let recordValue = action.propRecord[this.PropName];
        let actionProp = this.getActionProp(action);
        let propType = actionProp.type;
        let endValue;

        if (propType === PropType.from) {
            endValue = recordValue;
        } else if (propType === PropType.to || propType === PropType.free) {
            if (actionProp.valueType === ValueType.absolute) {
                endValue = actionProp.valueTo;
            } else if (actionProp.valueType === ValueType.relative) {
                endValue = Tools.add(recordValue, actionProp.valueTo);
            }
        }

        return Tools.getClone(endValue);
    }

    /**
     * 处理动作属性 填充tween
     */
    public static handleProp(action: Action): Tween<Node> {
        let duration = action.duration;
        let prop = this.getActionProp(action);
        let valueType = prop.valueType;
        let endValue = this.getEndValue(action);

        return this.craeteTween(action, duration, valueType, endValue, Easing[action.easing].toString());
    }

    /**
     * 创建tween函数
     * @param action 动作类
     * @param duration 动作时长
     * @param valueType 动作属性值类型
     * @param endValue 动作结束值
     * @param easing 缓动函数
     * @returns tween
     */
    protected static craeteTween(action: Action, duration: number, valueType: ValueType, endValue: any, easing: string) {
        // console.log("craeteTween:", ValueType[valueType], this.PropName, endValue, easing);

        let props = {};
        props[this.PropName] = endValue;
        let opts = {};
        opts["easing"] = easing;

        return tween().to(duration, props, opts);
    }

}

class OpacityPropHandler extends BasePropHandler {
    protected static PropName = "opacity";
    public static getActionProp(action: Action) {
        return action.opacityProp;
    }
    public static setPropValue(action: BaseAction, value: any) {
        this.getOpacityComp(action).opacity = value;
    }
    private static getOpacityComp(action: BaseAction) {
        let comp = action.node.getComponent(UIOpacity);
        if (!EDITOR && !comp) {
            error("ActionManager 开启透明度配置项的节点需有UIOpacity组件!");
        }
        return comp;
    }
    public static getPropValue(action: BaseAction) {
        let comp = this.getOpacityComp(action);
        return comp ? comp.opacity : 255;
    }
    public static recoverPropValue(action: BaseAction, value: any) {
        this.getOpacityComp(action).opacity = Tools.getClone(value);
    }

    protected static craeteTween(action: Action, duration: number, valueType: ValueType, endValue: any, easing: string) {
        // console.log("craeteTween opacity:", ValueType[valueType], endValue, easing);

        let start = this.getPropValue(action);
        let dis = endValue - start;
        let opacityComp = action.node.getComponent(UIOpacity);

        let opts = {};
        opts["easing"] = easing;
        opts["onUpdate"] = (target: BaseAction, ratio: number) => {
            opacityComp.opacity = start + dis * ratio;
        };

        return tween().to(duration, endValue, opts);
    }
}

class PositionPropHandler extends BasePropHandler {
    protected static PropName = "position";

    public static getActionProp(action: Action) {
        return action.positionProp;
    }
}
class ScalePropHandler extends BasePropHandler {
    protected static PropName = "scale";
    public static getActionProp(action: Action) {
        return action.scaleProp;
    }
}
class AnglePropHandler extends BasePropHandler {
    protected static PropName = "angle";
    public static getActionProp(action: Action) {
        return action.angleProp;
    }
}